package vip.liux.contracts.models.identity;

import com.fasterxml.jackson.annotation.JsonManagedReference;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import vip.liux.domain.entities.auditing.AuditedAggregateRoot;
import vip.liux.users.IUser;

import java.time.Instant;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

/**
 * 用户
 */
@Getter
@Setter
@Entity
@Table(name = "identity_users", indexes = {
        @Index(name = "ix_identity_users_user_name", columnList = "user_name"),
        @Index(name = "ix_identity_users_normalized_user_name", columnList = "normalized_user_name"),
        @Index(name = "ix_identity_users_email", columnList = "email"),
        @Index(name = "ix_identity_users_normalized_email", columnList = "normalized_email")
})
public class IdentityUser extends AuditedAggregateRoot<Long, IdentityUser> implements IUser<Long> {
    /**
     * 租户ID
     */
    @Column(name = "tenant_id", length = 36)
    private String tenantId;
    /**
     * 用户名或邮箱
     */
    @Column(name = "user_name", nullable = false, length = 256)
    private String username;
    /**
     * 规范化用户名，转成了大写
     */
    @Column(name = "normalized_user_name", nullable = false, length = 256)
    private String normalizedUserName;
    /**
     * 名字
     */
    @Column(name = "name", length = 64)
    private String name;
    /**
     * 姓氏
     */
    @Column(name = "surname", length = 64)
    private String surname;
    /**
     * 邮箱
     */
    @Column(name = "email", nullable = false, length = 256)
    private String email;
    /**
     * 规范化邮箱名，转成了大写
     */
    @Column(name = "normalized_email", nullable = false, length = 256)
    private String normalizedEmail;
    /**
     * 验证邮件确认，默认为false
     */
    @Column(name = "email_confirmed", nullable = false)
    private boolean emailConfirmed = false;
    /**
     * 密码哈希
     */
    @Column(name = "password_hash", length = 256)
    private String passwordHash;
    /**
     * 安全标记，Guid类型，用户凭据更改时生成随机值，如更改用户名
     */
    @Column(name = "security_stamp", nullable = false, length = 256)
    private String securityStamp;
    /**
     * 是否外部用户
     */
    @Column(name = "is_external", nullable = false)
    private boolean external = false;
    /**
     * 电话
     */
    @Column(name = "phone_number", length = 16)
    private String phoneNumber;
    /**
     * 电话确认，默认为false
     */
    @Column(name = "phone_number_confirmed", nullable = false)
    private boolean phoneNumberConfirmed = false;
    /**
     * 是否激活
     */
    @Column(name = "is_active", nullable = false)
    private boolean active = false;
    /**
     * 启用双因素认证
     */
    @Column(name = "two_factor_enabled", nullable = false)
    private boolean twoFactorEnabled = false;
    /**
     * 锁定的到期日期,null表示没有锁定
     */
    @Column(name = "lockout_end")
    private Instant lockoutEnd;
    /**
     * 是否可以被锁定
     */
    @Column(name = "lockout_enabled", nullable = false)
    private boolean lockoutEnabled = false;
    /**
     * 登陆失败的次数, 确定是否锁定用户
     */
    @Column(name = "access_failed_count", nullable = false)
    private int accessFailedCount;

    @OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    private final Set<IdentityUserLogin> logins;

    @OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    private final Set<IdentityUserToken> tokens;

    /**
     * JoinTable和JoinColumn一般定义在拥有关系的这一端，而mappedBy一定是定义在关系的被拥有方
     */
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "identity_user_roles",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id"),
            foreignKey = @ForeignKey(name = "fk_identity_user_roles_identity_users_user_id"),
            inverseForeignKey = @ForeignKey(name = "fK_identity_user_roles_identity_roles_role_id"))
    @JsonManagedReference
    private final Set<IdentityRole> roles;

    public IdentityUser() {
        super();
        this.logins = new HashSet<>();
        this.tokens = new HashSet<>();
        this.roles = new HashSet<>();
        securityStamp = UUID.randomUUID().toString();
    }

    public IdentityUser(Long id) {
        super(id);
        this.logins = new HashSet<>();
        this.tokens = new HashSet<>();
        this.roles = new HashSet<>();
    }

    @Override
    public boolean isEmailVerified() {
        return emailConfirmed;
    }

    @Override
    public boolean isPhoneNumberVerified() {
        return phoneNumberConfirmed;
    }
}